print0=print
import datetime
import sys,os,time
import threading
#from multiprocessing import Queue
#from multiprocessing import Pool,Manager
"""as 
不支持多进程，多进程操作同一个日志文件会有些乱，但是没太大问题
"""
#import fcntl
from queue import Queue
from copy import deepcopy
def Singleton(cls):
    _instance = {}
    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


import threading


class ThreadSafeBoolean:
    def __init__(self, initial_value=False):
        self._value = initial_value
        self._lock = threading.Lock()  # 创建锁对象

    def get(self):
        """获取当前值（线程安全）"""
        with self._lock:
            return self._value

    def set(self, new_value):
        """设置新值（线程安全）"""
        if not isinstance(new_value, bool):
            raise ValueError("Value must be a boolean")

        with self._lock:
            self._value = new_value

    def toggle(self):
        """切换值（取反，线程安全）"""
        with self._lock:
            self._value = not self._value
            return self._value
isdetails=ThreadSafeBoolean(False)
@Singleton
class Log(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.logQueue = Queue()
        # self.logQueue_file = Queue()
        self.details=isdetails
        self.logfreq=""
        self.logfile=""
        self.running=True
        self.isfile=False
        self.pid=os.getpid()
        self.isscreen=True

        self.level_info={"debug":5,"info":4,"warning":3,"sys":2,"error":1}
        self.level = "info"
        self.level_n=4
    def get_level_n(self,level=None):
        if level is None:
            level=self.level
        if isinstance(level,str):
            if level in self.level_info.keys():
                n=self.level_info[level]
            else:
                n=4
        else:
            n=4
        return n

    def run(self):
        while self.running:
            isempty=False
            try:
                isempty=self.logQueue.empty()
            except Exception as e:
                # self.__print_imp(e)
                self.running = False

            if isempty:
                a = threading.enumerate()
                t = a[0]
                if t.name== 'MainThread':
                    if t.is_alive():
                        time.sleep(0.5)
                    else:
                        #print0("主进程not running print退出")
                        self.deal()
                        self.running = False
                else:
                    time.sleep(0.5)
                # print("a",len(a))
                # isrunning=False
                # print("currentThread", threading.currentThread().name)
                # n=1
                # for t in a:
                #     if t.getName()!=threading.currentThread().name:
                #         print("t", n,t.getName(), t.is_alive())
                #         n = n + 1
                # #t = a[0]
                # #print0(a, t.getName(), t.is_alive())
                #     #if t.getName() == 'MainThread':
                #
                #         if t.is_alive():
                #             isrunning=True
                #     # else:
                #     #     print("t", n,t.getName(), t.is_alive())
                #     #     n = n + 1
                # if isrunning:
                #     time.sleep(0.5)
                # else:
                #     print0("主进程not running print退出")
                #     self.deal()
                #     self.running = False
            else:
                self.deal()
    def deal(self):
        import datetime
        if self.isfile:
            data_tmp=[]
            while not self.logQueue.empty():
                    data = self.logQueue.get()
                    data_tmp.append(data)
                    # if self.isscreen:
                    #     data=data[1]
                    #     if self.details.get():
                    #         t = data[0]
                    #         print0(t[0], t[1], t[2], data[1])
                    #     else:
                    #         print0(data[0], data[1])
            if data_tmp:
                with open(self.logfile, 'a', encoding='utf-8') as fp:
                    for data in data_tmp:
                        if self.details.get():
                            t = data[0]
                            if isinstance(t,tuple):
                                if len(t)==3:
                                    print0(t[0], t[1], t[2], data[1], file=fp)
                                else:
                                    print0(data[0], data[1], file=fp)
                            else:
                                print0(data[0], data[1], file=fp)
                        else:
                            print0(data[0], data[1], file=fp)
                if self.isscreen:
                    for data in data_tmp:
                        # if self.details.get():
                        #     t = data[0]
                        #     print0(t[0], t[1], t[2], data[1])
                        # else:
                        print0(data[0], data[1])
        else:
            while not self.logQueue.empty():
                data = self.logQueue.get()
                # if self.details.get():
                #     t = data[0]
                #     print0(t[0], t[1], t[2], data[1])
                # else:
                print0(data[0], data[1])
    def deal0(self,data):
        import datetime
        if self.isfile:
            with open(self.logfile, 'a', encoding='utf-8') as fp:
                if self.isscreen:
                    # if self.details.get():
                    #     t = data[0]
                    #     print0(t[0], t[1], t[2],  data[1])
                    # else:
                    print0(data[0], data[1])
                # if self.details.get():
                #     t = data[0]
                #     print0(t[0], t[1], t[2], data[1], file=fp)
                # else:
                print0(data[0], data[1], file=fp)
                # print0(data, file=fp)
        else:
            # if self.details.get():
            #     t = data[0]
            #     print0(t[0], t[1], t[2], data[1])
            # else:
            print0(data[0], data[1])
    def stop(self):
        self.running = False
    def setconfig(self,config):
        details=False
        if "details" in config.keys():
            if isinstance(config["details"],str):
                if config["details"] in ["true","True"]:
                    details=True
                elif config["details"]:
                    details=True
        if "logfile" in config.keys():
            self.logfile=config["logfile"]
        if "logfreq" in config.keys():
            self.logfreq=config["logfreq"]
        self.isscreen=config.get("isscreen",True)
        self.config(self.logfile,self.logfreq,self.isscreen,details)
    def create_logfile_name(self):
        isyes = False
        if self.logfile != "":
            self.logfile=os.path.abspath(self.logfile)
            root=os.path.dirname(self.logfile)
            if os.path.exists(root):
                if os.access(root, os.W_OK):
                    isyes = True
            else:
                try:
                    os.makedirs(root)
                    isyes = True
                except Exception as e:
                    self.__print_imp(e)
                    isyes = False
            if isyes:
                now = datetime.datetime.now()
                if self.logfile[-4:]==".log":
                    self.logfile=self.logfile[:-4]
                if self.logfreq in ["d", "day"]:
                    self.logfile = self.logfile + now.strftime("%Y%m%d") + ".log"
                elif self.logfreq in ["m", "month"]:
                    self.logfile = self.logfile + now.strftime("%Y%m") + ".log"
                elif self.logfreq in ["y", "year"]:
                    self.logfile = self.logfile + now.strftime("%Y") + ".log"
                else:
                    self.logfile = self.logfile + ".log"
            else:
                self.logfile = "log.log"
            self.isfile=True
    def config(self,logfile="",logfreq="",isscreen=True,details=False,level="info"):
        self.logfile=logfile
        self.logfreq = logfreq
        self.details.set(details)
        self.isscreen=isscreen
        self.level=level
        self.level_n=self.get_level_n(level)
        self.create_logfile_name()
    def print(self,*arg):
        flag=arg[0]
        n_flag=self.get_level_n(flag)
        if n_flag<=self.level_n:
            try:
                arg0=deepcopy(arg)
            except Exception as e:
                print0("print deepcopy",e)
                print0("print deepcopy arg", arg)
                arg0=arg
            if self.details.get():
                # t = threading.current_thread()
                # info="pid:"+str(self.pid)+" ident:"+str(t.ident)+" "+t.name
                filename=sys._getframe().f_back.f_code.co_filename
                funcName = sys._getframe().f_back.f_code.co_name #获取调用函数名
                lineNumber = sys._getframe().f_back.f_lineno     #获取行号
                #fname=sys._getframe().f_back.f_back.f_code.co_name # 获取当前函数名
                tt=datetime.datetime.now()
                #tt=tt.strftime("%Y-%m-%d %H:%M:%S.%f")
                tt=(tt,filename,funcName,lineNumber)
            else:
                tt = datetime.datetime.now()
                #tt = tt.strftime("%Y-%m-%d %H:%M:%S.%f")
            if self.running:
                self.logQueue.put((tt,arg0))
            else:
                self.deal0((tt,arg0))

        # self.running=True
    def print_imp_detail(self, arg,filename,funcName,lineNumber):
        try:
            arg0 = deepcopy(arg)
        except Exception as e:
            print0("print deepcopy",e)
            arg0 = arg
        tt = datetime.datetime.now()
        tt = (tt, filename, funcName, lineNumber)
        if self.running:
            self.logQueue.put((tt, arg0))
        else:
            self.deal0((tt, arg0))
    def print_imp_detail_no(self, arg):
        try:
            arg0 = deepcopy(arg)
        except Exception as e:
            print0("print deepcopy",e)
            arg0 = arg
        tt = datetime.datetime.now()
        if self.running:
            self.logQueue.put((tt, arg0))
        else:
            self.deal0((tt, arg0))

class Loglog(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.logQueue = Queue()
        # self.details=False
        self.details=isdetails
        self.logfreq=""
        self.logfile=""
        self.running=True
        self.isfile=False
        self.pid=os.getpid()
        self.isscreen=True
        self.level="info"
        self.level_info={"debug":5,"info":4,"warning":3,"sys":2,"error":1}
        self.level_n=4
    def get_level_n(self,level=None):
        if level is None:
            level=self.level
        if isinstance(level,str):
            if level in self.level_info.keys():
                n=self.level_info[level]
            else:
                n=4
        else:
            n=4
        return n

    def run(self):
        while self.running:
            isempty=False
            try:
                isempty=self.logQueue.empty()
            except Exception as e:
                #self.__print_imp(e)
                self.running = False
            if isempty:
                a = threading.enumerate()
                t = a[0]
                if t.getName() == 'MainThread':
                    if t.is_alive():
                        time.sleep(0.5)
                    else:
                        #print0("主进程not running print退出")
                        self.deal()
                        self.running = False
                else:
                    time.sleep(0.5)
                # print("a",len(a))
                # isrunning=False
                # print("currentThread", threading.currentThread().name)
                # n=1
                # for t in a:
                #     if t.getName()!=threading.currentThread().name:
                #         print("t", n,t.getName(), t.is_alive())
                #         n = n + 1
                # #t = a[0]
                # #print0(a, t.getName(), t.is_alive())
                #     #if t.getName() == 'MainThread':
                #
                #         if t.is_alive():
                #             isrunning=True
                #     # else:
                #     #     print("t", n,t.getName(), t.is_alive())
                #     #     n = n + 1
                # if isrunning:
                #     time.sleep(0.5)
                # else:
                #     print0("主进程not running print退出")
                #     self.deal()
                #     self.running = False
            else:
                self.deal()
    def deal(self):
        if self.isfile:
            with open(self.logfile, 'a', encoding='utf-8') as fp:
                while not self.logQueue.empty():
                    data = self.logQueue.get()
                    if self.isscreen:
                        if self.details.get():
                            t = data[0]
                            print0(t[0], t[1], t[2], t[3], data[1])
                        else:
                            print0(data[0], data[1])
                    if self.details.get():
                        t = data[0]
                        print0(t[0], t[1], t[2], t[3], data[1], file=fp)
                    else:
                        print0(data[0], data[1], file=fp)
                    # print0(data, file=fp)
        else:
            while not self.logQueue.empty():
                data = self.logQueue.get()
                if self.details.get():
                    t = data[0]
                    print0(t[0], t[1], t[2], t[3], t[4], data[1])
                else:
                    print0(data[0], data[1])
    def deal0(self,data):
        if self.isfile:
            with open(self.logfile, 'a', encoding='utf-8') as fp:
                if self.isscreen:
                    if self.details.get():
                        t = data[0]
                        print0(t[0], t[1], t[2], t[3], data[1])
                    else:
                        print0(data[0], data[1])
                if self.details.get():
                    t = data[0]
                    print0(t[0], t[1], t[2], t[3], data[1], file=fp)
                else:
                    print0(data[0], data[1], file=fp)
                # print0(data, file=fp)
        else:
            if self.details.get():
                t = data[0]
                print0(t[0], t[1], t[2], t[3], t[4], data[1])
            else:
                print0(data[0], data[1])
    def stop(self):
        self.running = False
    def setconfig(self,config):
        details=False
        if "details" in config.keys():
            if isinstance(config["details"],str):
                if config["details"] in ["true","True"]:
                    details=True
                elif config["details"]:
                    details = True
        if "logfile" in config.keys():
            self.logfile=config["logfile"]
        if "logfreq" in config.keys():
            self.logfreq=config["logfreq"]
        self.isscreen=config.get("isscreen",True)
        self.config(self.logfile,self.logfreq,self.isscreen,details)
    def config(self,logfile="",logfreq="",isscreen=True,details=False,level="info"):
        self.logfile=logfile
        self.logfreq = logfreq
        self.details.set(details)
        self.isscreen=isscreen
        self.level=level
        self.level_n=self.get_level_n(level)
        isyes = False
        if self.logfile != "":
            self.logfile=os.path.abspath(self.logfile)
            root=os.path.dirname(self.logfile)
            if os.path.exists(root):
                if os.access(root, os.W_OK):
                    isyes = True
            else:
                try:
                    os.makedirs(root)
                    isyes = True
                except Exception as e:
                    self.__print_imp(e)
                    isyes = False
            if isyes:
                now = datetime.datetime.now()
                if self.logfile[-4:]==".log":
                    self.logfile=self.logfile[:-4]
                if self.logfreq in ["d", "day"]:
                    self.logfile = self.logfile + now.strftime("%Y%m%d") + ".log"
                elif self.logfreq in ["m", "month"]:
                    self.logfile = self.logfile + now.strftime("%Y%m") + ".log"
                elif self.logfreq in ["y", "year"]:
                    self.logfile = self.logfile + now.strftime("%Y") + ".log"
                else:
                    self.logfile = self.logfile + ".log"
            else:
                self.logfile = "log.log"
            self.isfile=True

    def print(self,*arg):
        flag=arg[0]
        if self.get_level_n(flag)<=self.level_n:
            try:
                arg0=deepcopy(arg)
            except Exception as e:
                print0("print deepcopy",e)
                print0("print deepcopy arg", arg)
                arg0=arg
            if self.details.get():
                t = threading.currentThread()
                info="pid:"+str(self.pid)+" ident:"+str(t.ident)+" "+t.getName()
                filename=sys._getframe().f_back.f_code.co_filename
                funcName = sys._getframe().f_back.f_code.co_name #获取调用函数名
                lineNumber = sys._getframe().f_back.f_lineno     #获取行号
                #fname=sys._getframe().f_back.f_back.f_code.co_name # 获取当前函数名
                tt=datetime.datetime.now()
                #tt=tt.strftime("%Y-%m-%d %H:%M:%S.%f")
                tt=(tt,info,filename,funcName,lineNumber)
            else:
                tt = datetime.datetime.now()
                #tt = tt.strftime("%Y-%m-%d %H:%M:%S.%f")
            if self.running:
                self.logQueue.put((tt,arg0))
            else:
                self.deal0((tt,arg0))
            # self.running=True
log = Log()
print=log.print
log_set={}
def config(logfile="",logfreq="",isscreen=True,details=False,level="info",logname="default",max_logfiles=2):
    global log
    if logfile!="":
        if max_logfiles:
            d0=os.path.dirname(logfile)
            if os.path.exists(d0):
                fname=os.path.basename(logfile)
                l=os.listdir(d0)
                s=[]
                for i in l:
                    if fname in i:
                        s.append(i)
                s.sort()
                n=len(s)-max_logfiles
                if n>0:
                    for i in range(n):
                        j=s.pop(0)
                        url=os.path.join(d0,j)
                        os.remove(url)
                        print("remove",url)
    if logname=="default":
        log.config(logfile,logfreq,isscreen,details,level)
        return print
    else:
        logt = Loglog()
        logt.start()
        logt.config(logfile, logfreq, isscreen, details, level)
        log_set[logname]=logt
        return logt.print
def setconfig_byjson(config):
    log.setconfig(config)
def setconfig_bysys(config):
    pass
def endprint():
    log.stop()
    for i in log_set.values():
        i.stop()
#log.setDaemon(True)
log.start()
# #log.join()